package jaspect.control;

import jaspect.model.*;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

/**
 *
 * @author Arjun
 */
public class ConfigurationBuilder {

    private String fileName;
    private String projectDir;
    private Map<String, AspectConfiguration> aspects = new HashMap<String, AspectConfiguration>();

    /**
     *
     * @param path
     * @throws ParserConfigurationException
     * @throws SAXException
     * @throws IOException
     */
    public void buildConfigurations(String path) throws ParserConfigurationException, SAXException, IOException {
        setFileName(path);
        DocumentBuilderFactory fac = DocumentBuilderFactory.newInstance();
        DocumentBuilder db = fac.newDocumentBuilder();
        Document doc = db.parse(new File(path));
        NodeList lst = doc.getElementsByTagName("aspect");
        for (int i = 0; i < lst.getLength(); i++) {
            Node n = lst.item(i);
            AspectConfiguration c = parseAspectNode(n);
            aspects.put(c.getName(), c);
        }
    }

    /**
     *
     * @param aspectName
     * @return
     */
    public AspectConfiguration getAspectConfiguration(String aspectName) {
        return aspects.get(aspectName);
    }

    /**
     * @return the fileName
     */
    public String getFileName() {
        return fileName;
    }

    /**
     * @param fileName the fileName to set
     */
    public void setFileName(String fileName) {
        this.fileName = fileName;
    }

    /**
     * @return the aspects
     */
    public Map<String, AspectConfiguration> getAspects() {
        return aspects;
    }

    /**
     * @param aspects the aspects to set
     */
    public void setAspects(Map<String, AspectConfiguration> aspects) {
        this.aspects = aspects;
    }

    /**
     *
     * @param n
     * @return
     */
    public AspectConfiguration parseAspectNode(Node n) {

        AspectConfiguration c = new AspectConfiguration();
        Map<String, AspectMethod> mt = new HashMap<String, AspectMethod>();
        NamedNodeMap am = n.getAttributes();
        Node nn = am.getNamedItem("name");
        String name = nn.getNodeValue();

        c.setName(name);
        Node sn = am.getNamedItem("class");
        String sc = sn.getNodeValue();
        c.setServiceClassName(sc);
        Node im = am.getNamedItem("intercept-mode");
        String mode = im.getNodeValue();
        c.setMode(AspectConfiguration.InterceptMode.valueOf(mode));

        NodeList lst = n.getChildNodes();

        for (int i = 0; i < lst.getLength(); i++) {

            Node chn = lst.item(i);

            if ("params".equals(chn.getNodeName())) {

                NodeList chnl = chn.getChildNodes();

                for (int j = 0; j < chnl.getLength(); j++) {

                    Node cn = chnl.item(j);

                    if ("param".equals(cn.getNodeName())) {

                        NamedNodeMap cam = cn.getAttributes();
                        Node cnn = cam.getNamedItem("name");
                        String cname = cnn.getNodeValue();
                        String cval = cn.getFirstChild().getNodeValue();
                        c.getParams().setProperty(cname, cval);
                    }
                }
            } else if ("methods".equals(chn.getNodeName())) {

                NodeList chnl = chn.getChildNodes();

                for (int j = 0; j < chnl.getLength(); j++) {

                    Node cn = chnl.item(j);
                    AspectMethod amt = new AspectMethod();

                    if ("method".equals(cn.getNodeName())) {

                        Properties arg = new Properties();
                        NamedNodeMap cam = cn.getAttributes();
                        Node cnn = cam.getNamedItem("name");
                        String cname = cnn.getNodeValue();

                        amt.setMethodName(cname);

                        Node mi = cn.getFirstChild();
                        mi = mi.getNextSibling();

                        if ("method-params".equals(mi.getNodeName())) {

                            NodeList chnll = mi.getChildNodes();

                            for (int k = 0; k < chnll.getLength(); k++) {

                                Node cn1 = chnll.item(k);

                                if ("class-fields".equals(cn1.getNodeName())) {

                                    NamedNodeMap nm = cn1.getAttributes();
                                    Node mn = nm.getNamedItem("full");
                                    String bv = mn.getNodeValue();

                                    amt.setAllClassFields(Boolean.getBoolean(bv));
                                } else if ("param".equals(cn1.getNodeName())) {
                                    NamedNodeMap nm = cn1.getAttributes();
                                    Node mn = nm.getNamedItem("name");
                                    String mname = mn.getNodeValue();
                                    Node mv = nm.getNamedItem("type");
                                    String mtype = mv.getNodeValue();

                                    arg.setProperty(mname, mtype);
                                }
                            }
                        }
                        amt.setArguments(arg);
                        mt.put(amt.getMethodName(), amt);
                    }
                }
            }
        }

        c.setMethods(mt);

        return c;
    }

    /**
     *
     * @param a
     * @return
     */
    public String toAspectXML(AspectConfiguration a) {

        StringBuilder sb = new StringBuilder();

        sb.append("<aspect name=\"");
        sb.append(a.getName());
        sb.append("\" class=\"");
        sb.append(a.getServiceClassName());
        sb.append("\" intercept-mode=\"");
        sb.append(a.getMode().toString());
        sb.append("\">");
        sb.append("\n\t\t<params>\n");

        Properties pe = a.getParams();
        Enumeration en = pe.keys();

        for (int i = 0; i < pe.size(); i++) {
            String se = en.nextElement().toString();
            sb.append("\t\t\t<param name=\"");
            sb.append(se);
            sb.append("\">");
            sb.append(pe.getProperty(se.toString()));
            sb.append("\n\t\t\t</param>\n");
        }
        sb.append("\t\t</params>\n");
        sb.append("\t\t<methods>\n");

        Map<String, AspectMethod> le = a.getMethods();

        for (AspectMethod am : le.values()) {
            String se = aspectMethodToAspectXML(am);

            sb.append(se);

        }

        sb.append("\t\t</methods>\n");
        sb.append("\t</aspect>\n");

        return sb.toString();
    }

    /**
     *
     * @param a
     * @return
     */
    public String aspectMethodToAspectXML(AspectMethod a) {
        StringBuilder sb = new StringBuilder();
        sb.append("\t\t\t<method name=\"");
        sb.append(a.getMethodName());
        sb.append("\">\n");
        sb.append("\t\t\t\t<method-params>");
        sb.append("\n\t\t\t\t\t<class-fields full=\"");
        sb.append(a.getAllClassFields().toString());
        sb.append("\"></class-fields>\n");

        Properties pe = a.getArguments();
        Enumeration en = pe.keys();

        for (int i = 0; i < pe.size(); i++) {
            String se = en.nextElement().toString();
            sb.append("\t\t\t\t\t<param name=\"");
            sb.append(se);
            sb.append("\" type=\"");
            sb.append(pe.getProperty(se.toString()));
            sb.append("\"></param>\n");

        }
        sb.append("\t\t\t\t</method-params>\n");
        sb.append("\t\t\t</method>\n");

        return sb.toString();
    }

    /**
     *
     * @throws Exception
     */
    public void writeToFile() throws Exception {
        StringBuilder sb = new StringBuilder();
        sb.append("<aspects>\n");
        for (AspectConfiguration a : aspects.values()) {
            sb.append("\t");
            sb.append(toAspectXML(a));
        }
        sb.append("</aspects>\n");
        String xml = sb.toString();
        if (this.getProjectDir() != null) {
            BufferedWriter out = new BufferedWriter(new FileWriter(this.getProjectDir() + "\\" + "out.ajxml"));
            out.write(xml);
            out.close();
        }


    }

    /**
     *
     * @param args
     * @throws Throwable
     */
    public static void main(String args[]) throws Throwable {

        String path = "G:\\project\\db1.ajxml";
        ConfigurationBuilder b = new ConfigurationBuilder();
        b.buildConfigurations(path);
        //System.out.println(b.getAspects());
    }

    /**
     * @return the projectDir
     */
    public String getProjectDir() {
        return projectDir;
    }

    /**
     * @param projectDir the projectDir to set
     */
    public void setProjectDir(String projectDir) {
        this.projectDir = projectDir;
    }
}

